import { genInlineComponentText, stripScript, stripStyle, stripTemplate } from './parser';
const os = require('os');

export function render(content: string, options: any) {
    const { componentsDeclaration, styles, templates } = parseComponents(content, options);
    const script = getScriptOutput(content, componentsDeclaration, options);
    const style = getStyleOutput(styles, options);
    return {
        template: templates.join(''),
        script,
        style
    }
}

function parseComponents(content: string, options: any) {
    let start = 0;
    let id = 0;
    const startTag = '<!--vue-demo:';
    const endTag = ':vue-demo-->';
    const startTagLength = startTag.length;
    const endTagLength = endTag.length;
    let commentStart = content.indexOf(startTag);
    let commentEnd = content.indexOf(endTag, commentStart + startTagLength);
    let hasComment = commentStart > -1 && commentEnd > -1;
    const templates: string[] = [];
    const styles: string[] = [];
    let componentsDeclaration = '';
    while (hasComment) {
        const fragmentBeforeTheCommentStart = start;
        const fragmentBeforeTheCommentEnd = commentStart;
        const fragmentBeforeTheComment = content.slice(fragmentBeforeTheCommentStart, fragmentBeforeTheCommentEnd);
        templates.push(fragmentBeforeTheComment);

        const commentFragmentStart = commentStart + startTagLength;
        const commentFragmentEnd = commentEnd;
        const fragmentInComment = content.slice(commentFragmentStart, commentFragmentEnd);

        const html = stripTemplate(fragmentInComment);
        const script = stripScript(fragmentInComment, `render-demo-${id}-script`);
        const style = stripStyle(fragmentInComment);
        const demoComponentContent = genInlineComponentText(html, script, options, `inline-component-${id}`);
        const demoComponentName = `render-demo-${id}`;
        templates.push(`<${demoComponentName} />`);
        componentsDeclaration += `${JSON.stringify(demoComponentName)}: ${demoComponentContent},`;
        styles.push(style);

        start = commentFragmentEnd + endTagLength;
        const nextCommentFragmentStart = content.indexOf(startTag, start);
        const fragmentAfterTheCommentStart = start;
        const fragmentAfterTheCommentEnd = nextCommentFragmentStart > -1 ? nextCommentFragmentStart : content.length - 1;
        const fragmentAfterTheComment = content.slice(fragmentAfterTheCommentStart, fragmentAfterTheCommentEnd);
        templates.push(fragmentAfterTheComment);
        start = fragmentAfterTheCommentEnd;

        commentStart = nextCommentFragmentStart;
        commentEnd = content.indexOf(endTag, commentStart + startTagLength);
        hasComment = commentStart > -1 && commentEnd > -1;
        id++;
    }
    return { componentsDeclaration, styles, templates };
}

function getScriptOutput(content: string, componentDeclaration: string, options: any): string {
    let scriptToOutput = '';
    if (componentDeclaration) {
        scriptToOutput = `
        <script lang="ts">
            import * as Vue from 'vue';
            ${(options?.scriptImports || []).join(os.EOL)}
            export default {
                name: 'component-doc',
                components: {
                    ${componentDeclaration}
                }
            };
        </script>`
    } else if (content.indexOf('<script>') === 0) {
        const scriptEnd = content.indexOf('</script>') + '</script>'.length;
        scriptToOutput = content.slice(0, scriptEnd);
    }
    return scriptToOutput;
}

function getStyleOutput(styles: any[], options: any): string {
    const stylesToOutput = [...new Set(styles)];
    const preprocessors = ['scss', 'sass', 'less', 'stylus']
    const _style = preprocessors.includes(options.cssPreprocessor) ?
        `style lang="${options.cssPreprocessor}"` :
        'style'
    const hasStyles = stylesToOutput && stylesToOutput.length > 0;
    return hasStyles ? `<${_style}>${stylesToOutput.join('')}</style>` : `<style></style>`;
}